רק כדי להציג עמוד יחיד זה השרת צריך להתחבר למסד, לעשות כמה שליפות, לקרוא כמה קבצי תבניות (mvc :) ולבצע עוד כמה חישובים. וכל פעם שאנחנו לוחצים ריענון השרת מחשב את כל זה מחדש שוב ושוב. למרות ששום דבר לא השתנה כאן בחצי שעה האחרונה.
למה צריך cache
בזבוז משאבים מיותר
בסה"כ רצינו להציג עמוד. אבל בפועל כל טעינה של העמוד מכריחה את השרת לייצר אותו מחדש, לחשב, לשלוף, להציג. וכשיש כמה גולשים במקביל? השרת מחשב את אותם תוצאות כל פעם מחדש ולפעמים את אותו החישוב כמה פעמים בשניה.
cache
במקום שהשרת שלנו יחשב או אותו עמוד כמה פעמים בשניה - למה שלא נשמור את תוצאות החישוב לזמן מסוים? מלבד מדריכי php, העמוד הזה מציג הודעות אחרונות מהפורום. התוכן של העמוד משתנה בערך אחת לחצי שעה.
באפשרותינו לשמור את תוצאות החישוב ( ה-html הסופי) למשך חצי שעה ופעם בחצי שעה לחשב את העמוד עוד פעם. באופן הזה במקום שהשרת יבצע את אותם חישובים כמה פעמים בשניה - הוא יבצע חישוב אחד פעם בחצי שעה.
שמירת העמוד
קודם כל עלינו לשמור את תוצאות החישובים (את ה-html הסופי) איפשהו למשך חצי שעה כדי שלא נחשב אותו כל פעם מחדש. אנחנו נעשה את זה בדרך הפשוטה ביותר - באמצעות קבצים שבהם נשמור את התוכן המוכן. בכל פעם שהעמוד ייטען - הסקריפט יבדוק האם יש קובץ עם html מוכן מהחצי שעה האחרונה. אם כן - יבצע פעולה אחת - קריאה מקובץ. אם לא - יבצע את שלל החישובים, השליפות וכל השאר.
output buffering
בדרך כלל כל הפלט של העמוד שלנו נשלח ישירות
בעזרת buffering אפשר לקבל את הפלט הסופי לתוך משתנה שנוכל לשמור בקובץ לשימוש עתידי. למזלנו php מספקת כלים ממש פשוטים לעבודה עם buffering בעזרת כמה פונקציות פשוטות.
פונקציות ob
לא נרחיב על פונקציות ה-buffering ב-php שיש לא הרבה כאלה ונתמקד רק באחת החשובה שבהם. ob_start
כשקוראים לפונקציה הזו, היא מאתחלת את זכרון ה-buffer ומתחילה לשמור כל פלט שבא אחריה לתוך הזכרון. ברגע שהסקריפט מסתיים - היא שולחת הלאה את כל הפלט שהצטבר.
<?php ob_start(); echo 'everything will be buffered first';
תכונה חשובה שלה, היא האפשרות להעביר פרמטר מסוים לפוקנציה הזו. הפרמטר שהיא מקבלת הוא שם של פונקציה שלה היא תקרא ממש לפני שהפלט נשלח הלאה (לדפדפן).
<?php
function callback($buffer) { return $buffer; }
ob_start('callback');
echo 'everything will be buffered first';
function callback($buffer) { return $buffer; }
ob_start('callback');
echo 'everything will be buffered first';
שימו לב, חשוב:
הפונקציה callback תתבצע ברגע ש-php תהיה מוכנה להעביר את כל הפלט לדפדפן (ברגע סיום העבודה של הסקריפט. הפונקציה callback בתור פרמטר מקבלת את כל התוכן שנשמר ב-buffer ומיועד לשליחה.
הפונקציה יכולה לעשות שינויים על התוכן, עדכונים וכו' אבל הערך המוחזר מהפונקציה הוא יהיה הפלט החדש שיישלח לדפדפן.
לא הכל ל-cache אחד
ברוב המקרים הפלט של סקריפט אחד יהיה שונה באופן תלוי בפרמטרים. למשל עבור script.php?id=1 יהיה פלט כלשהו ועבור id=2 יהיה פלט שונה לחלוטין. למקרה הזה הדרך הטובה היא לשמור שני תוצאות ב cache ולהחזיר את התוצאה המתאימה לפרמטרים.
הקוד
<?php
error_reporting(E_ALL);
$_cache['time_to_cache'] = 1800;
$_cache['directory'] = 'cache';
function save_cache($data)
{
global $_cache;
chdir(dirname(__FILE__));
// create the cache file
if(!touch($_cache['url'])) return 'failed to create cache file. check directory permissions';
// write the data to the cache file
file_put_contents($_cache['url'], $data);
// the return value is being echoed
return $data;
}
function cache_by_query_string( )
{
global $_cache;
// get url file name (/directory/script.php?x=1&y=2)
$url = $_SERVER["PHP_SELF"] . '?' . $_SERVER["QUERY_STRING"];
// encode the url (%2Fdirectory%2Fscript.php%3Fx%3D1%26y%3D2)
$url = urlencode($url);
// find out the name of the cache file
// cache/%2Fdirectory%2Fscript.php%3Fx%3D1%26y%3D2.cache
$cache_file_name = $_cache['directory'] .'/'. $url . '.cache';
clearstatcache();
if( file_exists($cache_file_name) )
{
// last file's modification time
$last_cache_time = filectime($cache_file_name);
// if the file was modified in the last X seconds -> it's good
// otherwise, it's too old
if( $last_cache_time + $_cache['time_to_cache'] > time() )
{
ob_clean();
readfile($cache_file_name);
exit();
}
}
ob_start('save_cache');
$_cache['url'] = $cache_file_name;
}
// start the caching procedure
cache_by_query_string();
/********** ALL YOUR CODE GOES BELOW ************/
echo 'hey, hi, heya';
if( isset($_GET['id']) ) echo 'this page cached with id ', $_GET['id'];
error_reporting(E_ALL);
$_cache['time_to_cache'] = 1800;
$_cache['directory'] = 'cache';
function save_cache($data)
{
global $_cache;
chdir(dirname(__FILE__));
// create the cache file
if(!touch($_cache['url'])) return 'failed to create cache file. check directory permissions';
// write the data to the cache file
file_put_contents($_cache['url'], $data);
// the return value is being echoed
return $data;
}
function cache_by_query_string( )
{
global $_cache;
// get url file name (/directory/script.php?x=1&y=2)
$url = $_SERVER["PHP_SELF"] . '?' . $_SERVER["QUERY_STRING"];
// encode the url (%2Fdirectory%2Fscript.php%3Fx%3D1%26y%3D2)
$url = urlencode($url);
// find out the name of the cache file
// cache/%2Fdirectory%2Fscript.php%3Fx%3D1%26y%3D2.cache
$cache_file_name = $_cache['directory'] .'/'. $url . '.cache';
clearstatcache();
if( file_exists($cache_file_name) )
{
// last file's modification time
$last_cache_time = filectime($cache_file_name);
// if the file was modified in the last X seconds -> it's good
// otherwise, it's too old
if( $last_cache_time + $_cache['time_to_cache'] > time() )
{
ob_clean();
readfile($cache_file_name);
exit();
}
}
ob_start('save_cache');
$_cache['url'] = $cache_file_name;
}
// start the caching procedure
cache_by_query_string();
/********** ALL YOUR CODE GOES BELOW ************/
echo 'hey, hi, heya';
if( isset($_GET['id']) ) echo 'this page cached with id ', $_GET['id'];
את הקוד אפשר לשמור בקובץ נפרד ולעשות לו אינקלוד בתחילת כל סקריפט.
בתחילת העמוד מוגדרים שני פרמטרים.
הראשון הוא תיקיה שבה ישמרו קבצי ה-cache (בשביל זה ניצור תיקיה cache) והשני הוא זמן חיי ה-cache לפני שנחשיב אותו לישן מדי. כאן הוא מוגדר לחצי שעה.
בתחילת הטעינה של העמוד קוראים לפונקציה cache_by_query_string
היא בודקת האם קיים קובץ cache מתאים לעמוד שביקשו. אם כן הוא בודק את זמן יצירת הקובץ. אם הקובץ נוצר בחצי שעה האחרונה - זה אומר שהתוכן שלו עדיין עדכני ואפשר להשתמש בו.
אם הקובץ לא קיים, או שהוא כבר לא עדכני יותר - הוא מפעיל את מנגנון ה-buffering שבסיומו יקרא לפונקציה save_cache . הפונקציה save_cache מקבתל כפרמטר את התוכן של ה-buffer ושומרת אותו בקובץ.
אם הקובץ לא קיים touch יוצרת אותו. אם הוא כבר קיים - touch מרוקנת אותו ומעדכנת את זמן יצירת הקובץ. אל תוך הקובץ מוכנס כל התוכן הסופי של העמוד. בפעם הבאה שמשהו ירצה לרענן את העמוד - הקובץ הזה יהיה קיים והתוכן ישלף ממנו במקום שיחושב שוב פעם מחדש.
הרכבת שם הקובץ
שם הקובץ במערכת ההפעלה יהיה זהה לבקשה שעברה לשרת cache/myscript.php?id=3.cache
בגלל שתווים מסוימים לא יכולים להיות חלק של שם הקובץ. השתמשנו ב urlencode כדי להחליף אותם למשהו שיכול להיות חלק מקובץ ורשמנו אותו ככה.
ולסיכום
את זמן חיי ה-cache יש לבחור בהתאם לעמוד. עם זה עמוד עם תגובות ודברים אחרים שמתעדכנים לעתים קרובות - יש לבחור זמן נמוך יותר. למשל חישוב אחד פעם בחמש שניות מביא לגידול של מאות אחוזים בביצועים לאומת כמה חישובים בשניה אחת.
מצד שני שמירת תוצאות בקבצים היא לא הדרך היעילה ביותר. הדרך העדיפה ביותר היא שמירת הנתונים בזכרון ה-ram עם שימוש בתוכנת (שרתים) שמתאימות ומיועדות במיוחד בשביל זה כמו memcache או mysql עם טבלאות ה memory שלהם.
בעקבות בקשות הקהל — איך להשתמש בקוד
1. יוצרים קובץ חדש (למשל cache_manager.php) מעתיקים את כל הקוד מהכתבה לתוכו.
2. מוחקים מסוף הקוד את שני שורות ה echo . הם לא שייכות, הם סתם דוגמה.
3. מוסיפים inculde 'cache_manager.php' בתחילת כל הסקריפטים שגולשים אליהם
4. יוצרים תיקיה cache (ובאחסון נותנים לה הראשות כתיבה 0777)
5. מריצים ובודקים שבאמת נוצר קובץ עם תוכן בתיקיה cache
מתי לא להשתמש בזה:
בסקריפטים שהפלט שלהם שונה ממשתמש למשתמש (לפי קוקיז, סשן, ip)
בסקריפטים שמעבדים טפסים
בסקריפטים שלא עושים שום דבר מסובך ממילא
בזמן פיתוח. אחרת לא תראו שינויים שעשיתם בסקריפט.
תגובות לכתבה:
לא הצלחתי להבין איך להשתמש בזה.
אתה משקיע את מירב המאמאצים בכתיבה על מה שעומד מאחורי זה. אבל שוכח לכתוב בפועל איך להשתמש בה.
אשמח אם תוכל להביא דוגמאות ולהסביר ולהתעמק בהם.
כמו שאומרים - תמונה אחת שווה אלף מילים.
אהה תודה... אני יעדכן את המדריך.
המטרה שלי בכל אופן היא באמת להסביר את מה שעומד מאחורה ולא סתם לתת פה סקריפטים להורדה.
אם כבר מדברים על CACHE - באחד הפוסטים פה, רשמת שאתה משתמש ב TWIG. שמתי לב שלTWIG אין מנגנון מובנה לקאשינג בדומה למה שהראת למעלה. אני מניח שאתה כן משתמש בקאשינג לאתר - מה אתה עושה? מתמש במשהו קיים או שיצרת משהו משלך?
תודה.
דווקא ל-twig יש קש טיפה שונה. הוא מחבר את כל התבניות והמשתנים לסקריפטים רגילים שאחרי זה מתקמפלים על ידי zend optimizer
הקש היחידי שאני מימשתי הוא על תוכן הכתבה שבמקור מורכבת מ-bbcodes רגילים שמפוענחים רק פעם אחת. מבוסס על מסד ולא על קבצים
אלכס, תודה שהתייחסת לתגובה שלי. :)
אני לא אומר לך להביא מערכות או משהו בסגנון.
אני חושב שזה נושא מאוד חשוב ב PHP ושצריך להתעמק גם בעוד נושאים מלבד מה ש"עומד מאחורי".
אני מצפה בקוצר רוח לעדכון :]
ד"א אחלה אתר, באמת שיש בו מאמרים ממש טובים. :]
ככה שמבחינת יעילות וכד' אין הבדל (ז"א,זה מספיק יעיל)? אני לא צריך למצוא/לכתוב משהו כמו למעלה?
המטרה היא לחסוך את הפעילות הכבדה שיש לך. אם אין לך שום פעילות כבדה בעמוד - כנראה שאין טעם. למרות שאם הדפים שלך לא מתעדכנים יותר מדי - בכל זאת תוכל להרוויח מהשימוש בזה.
בגדול אתה תמיד תרוויח מזה, למעט מקרים שהעמוד שלך עושה פחות פעולות שהקוד בכתבה עושה. בשאר המקרים זה באמת רקלהעתיק את הקוד לקובץ חדש ולעשות לו אינקלוד בכל העמודים שאתה רוצה שישמרו בקש.
הוספתי הסבר קצר על "איך להשתמש".
יותר ברור? :)
ואם אני רוצה שרק מידע ספציפי יאוסחן ב cache?
ע"פ הדוגמה שראיתי אני מבין שמרגע שאתה מפעיל את הקוד - כל מה שמתחתיו ישמור את הנתונים.
נכון. במקרה הזה כל העמוד במלואו יישמר בקש. אם אתה רוצה לשמור רק חלק מהעמוד תצטרך להוריד את exit מהקוד,
לעשות לו אינקלוד בתחילת החלק שאתה רוצה שיישמר ובסוף החלק לקרוא לפוונקציה ob_end_flush()
שים לב שהפוקנציה כרגע בכל זאת מיועדת לעבוד עם פרמטרים שונים מתוך הקישור של העמוד. אם אתה רוצה לשמור בקש חלק מהעמוד והוא לא משתנה בהתאם לפרמטרים - תצטרך לשנות את הפונקציה עצמה.
אוקיי אני חושב שהבנתי. :]
תודה רבה.
לגולשים היקרים, השתמשו בקאש בעיקר בעמודים שדורשים זמן ניתוח לא קטן ועשו זאת רק בקבצים שאתם לא רוצים שבכל דקה ודקה באתר יראו את השינויים שקרו .
הקטע הוא להיות יעילים ולעשות את הקאשינג רק במקום שאתם צריכים ויודעים שזה יחסוך לכם שאילתות כבדות וכד' ..
כך לדוגמא, אל תשתמשו בקאש בעמודי חיפוש או בעדכונים האחרונים באתר חדשות שמביא עדכונים בכל דקה ושניה, שהרי חייבים את התוצאות העכשוויות ביותר .
דווקא עמודי חיפוש זה מקום מצוין לקאשינג עקב היותם כבדים ותחלופת תוצאות איטית. עמודים שמתעדכנים הרבה במזן קצר - גם הם ירוויחו מקאשינג אם לשנות את זמן חיי הקש ל10 שניות לדוגמה. בעיקר אתרי חדשות פופולאריים עם מאות גולשים במקביל.
ממש ממש לא .
וכמובן שאני מדבר על עמודי חיפוש שאתה חייב בהן תוצאות על הדקה כמו לדוגמא נושאים בפורומים, באתרי חדשות וכד' ..
עכשיו שמתי לב שכתבת לדוגמא 10 שניות .
תן לי להוסיף ולומר שאם מדובר באתר כבד שמבוקר הרבה אז במקרה כזה באמת רצוי למטמן לכ10 שניות עד חצי דקה .
העיקר להישאר בטווח של הדקה ולקבל את התוצאות המעודכנות ביותר .
לגולשים היקרים שמבינים אנגלית, מצאתי בשבילכם מאמר נהדר שגם מביא תרשימים שמחדדים יותר את העניין ואיך הוא עוזר לנו .
http://www.developertutorials.com/tutorials/php/php-caching-1370/
במקרה של עמוד חיפוש אתה יכול לטמון להרבה הרבה יותר מ10 שניות, כל עוד הקש תלוי בפרמטר החיפוש. למשל הקוד בכתבה הכן מתחשב בפרמטרים המועברים וייצור דפי קש שונים עבור הבקשה
search.php?id=car
וקש אחר עבור
search.php?id=bus
כן, ברור שהוא יתחשב בפרמטרים .
ולגבי שניות, זה כבר שיקול של בעל האתר .
אם אני באופן אישי אמטמן תוצאות חיפוש אני לא אעשה את זה ליותר מדי זמן .
אה, וכותבים "האבוד" .
איזה פאדיחות :)
שטויות, כולנו בני אדם ובני אדם טועים :)
מדריך מעולה))
אחרי שפירסמת את זה הבנתי שעדיף באמת לעבוד ככה.
לפני זה מדי פעם חשבתי אבל לא חשבתי שזה עד כדי כך עוזר))
בכ"מ, תודה על הפרסום. אזרתי קצת כוח וכתבתי מחלקה לזה.
אני באמת שמח לשמוע דברים כאלה.
רוצה אולי לפרסם את המחלקה שלך ? :)
אח, אני מתבייש להראות אותה בפומבי, אם בא לך שאני אשלח לך בפרטי בכיף))
לא השקעתי בה במיוחד אז זה בושה להראות כזה דבר :D
תפרסם אותה בכל זאת, תקבל פידבק וגם תהיה לך סיבה טובה מאוד לכתוב קוד תקין ויפה :)
איפה לפרסם? בתגובה פה אי אפשר, פה זה RTL ולא יהיה מובן.
בפורום בפונקציות מוכנות ותן פה קישור
עשיתי את זה באתר שלי.
אבל לא מורגש שינוי וגם אם אני מבצע שינויים בקבצים השינויים עדיין קוראים ישר ולא צריך לחכות.
למה זה ככה?
כנראה שהמנגנון לא הופעל אז.
נוצרים לך קבצים בתיקיה הזמנית? תנסה לדחוף echo לפונקציות בכל מיני מקומות, לראות שהפונקציות האלה באמת מופעלות.
לא הבנתי בדיוק השתמשתי בקוד שנתתת שמתי אותו באתר ונוצרים קבצים בתקיה.
ואם אתה לוקח את הקוד איך שהו, 1 ל 1, שם בקובץ ריק ומפעיל, זה עובד ?
לא הבנתתי את השאלה
1. להעתיק את הקוד לקובץ בשם blabla.php
2. להפעיל
3. לשנות את הטקסט
4. ללחוץ F5 ולראות את הטקסט הישן
הטקסט מגיע ממסד נתונים
אתה יכול לעשות מה שכתוב?
1. להעתיק את הקוד לקובץ בשם blabla.php
2. להפעיל
3. לשנות את הטקסט
4. ללחוץ F5 ולראות את הטקסט הישן
הרגע בדקתי, אין ב-4 סעיפים האלה שום דבר על מסד נתונים :/
להעתיק את הקוד מהקובץ הזמני או מהרגיל?
מאיזה קובץ? על מה אתה מדבר?
להעתיק מאתר לקובץ.
יש אצלי בעיה,
הקוד יוצר את התיקיה והכל טוב, אבל כל פעם שאני נכנס לעמוד הוא מעדכן את הקובץ cache מבלי לבדוק את הזמן.. כיאילו הוא מדלג על זה.